package be.rivendale.mathematics;

import org.apache.commons.lang.Validate;

/**
 * Provides several general purpose mathematical utility methods.
 */
public abstract class MathematicalUtilities {
    /**
     * The number of spaces after the decimal separator that we support.
     * This defines the accuracy of our calculations.
     * For example, with a value of 6, we have an accuracy of 0.000001.
     */
    public static final int NUMBER_OF_SYMBOLS_AFTER_DECIMAL_SEPARATOR = 6;

    /**
     * The <a href="http://en.wikipedia.org/wiki/Machine_epsilon">epsilon</a> to use for comparison of floating point numbers.
     */
    public static final double EPSILON = 1 / Math.pow(10, (double) NUMBER_OF_SYMBOLS_AFTER_DECIMAL_SEPARATOR);

	/**
     * Checks if two doubles are equal, using the predefined {@link #EPSILON} value
     * @param a The first double to compare
     * @param b The second double to compare
     * @return True if both are equal when using the specified delta.
     */
    public static boolean equals(double a, double b) {
        return Math.abs(a - b) < EPSILON;
    }

    /**
     * Checks that the betweenValue is between the lowValue and the highValue.
     * @param lowValue The lowest value (betweenValue must be greather then or equal to this)
     * @param highValue The highest value (betweenValue must be less then or equal to this)
     * @param betweenValue The value that is to be somewhere between the lowValue and the highValue
     * @return True if  lowValue <= betweenValue <= highValue
     */
    public static boolean between(double lowValue, double highValue, double betweenValue) {
        return lowValue <= betweenValue
                && highValue >= betweenValue;
    }

    /**
     * Checks if the specified dividend is actually dividable by the specified divisor.
     * With dividable is meant: having a integer as the division quotient.
     * For example: in <code>100 / 5 = 20</code> the divisor is <code>5</code> and the dividend is <code>100</code>.
     * @param divisor The divisor of to check.
     * @param dividend The dividend to check.
     * @return True if the divisor is a divisor of the dividend.
     */
    public static boolean isDivisorOf(int divisor, int dividend) {
        return dividend % divisor == 0;
    }

    /**
     * Checks if the two specified points are equal.
     * @param a A point to be tested for equality with b.
     * @param b A point to be tested for equality with a.
     * @return True if the points are equal. Fale otherwise.
     */
    public static boolean equals(Point a, Point b) {
        String errorMessage = "Unable to check if two points are equal if at least one of them is null";
        Validate.notNull(a, errorMessage);
        Validate.notNull(b, errorMessage);
        return equals(a.getX(), b.getX())
                && equals(a.getY(), b.getY())
                && equals(a.getZ(), b.getZ());
    }

	/**
	 * Calculates the <a href="http://en.wikipedia.org/wiki/Determinant">determinant</a> of the specified 3x3 matrix.
	 * The three specified triples can be interpreted as a 3x3 matrix since there are three triples of each three coordinates.
	 * <p>Weather these triples are laid out as columns in the imaginary matrix or as rows doesn't matter. This is due to the
	 * fact that the determinant is the same if they are positioned as rows or columns.</p>
	 * @param firstRowOrColumn The first set of 3 coordinates. If they are imagined as rows or columns in the matrix
	 * doesn't matter since the result would be the same.
	 * @param secondRowOrColumn The second set of 3 coordinates. If they are imagined as rows or columns in the matrix
	 * doesn't matter since the result would be the same.
	 * @param thirdRowOrColumn The third set of 3 coordinates. If they are imagined as rows or columns in the matrix
	 * doesn't matter since the result would be the same.
	 * @return The determinant of the 3x3 matrix formed by the three triples of three coordinates each
	 */
	public static double determinant(Triple firstRowOrColumn, Triple secondRowOrColumn, Triple thirdRowOrColumn) {
		String errorMessage = "Three vectors are required to calculate the determinant (these vectors form a 3x3 matrix)";
		Validate.notNull(firstRowOrColumn, errorMessage);
		Validate.notNull(secondRowOrColumn, errorMessage);
		Validate.notNull(thirdRowOrColumn, errorMessage);

		return firstRowOrColumn.getX() * secondRowOrColumn.getY() * thirdRowOrColumn.getZ()
				+ secondRowOrColumn.getX() * thirdRowOrColumn.getY() * firstRowOrColumn.getZ()
				+ thirdRowOrColumn.getX() * firstRowOrColumn.getY() * secondRowOrColumn.getZ()
				- firstRowOrColumn.getX() * thirdRowOrColumn.getY() * secondRowOrColumn.getZ()
				- secondRowOrColumn.getX() * firstRowOrColumn.getY() * thirdRowOrColumn.getZ()
				- thirdRowOrColumn.getX() * secondRowOrColumn.getY() * firstRowOrColumn.getZ();
	}
}
